﻿using ARuntime.BaseFramework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace ARuntime
{
    public class Engine
    {
        public byte[] variableArray = new byte[500000];

        public List<string> errorList = new List<string>();

        public List<object> Stack = new List<object>();

        public int thePointer = 0;

        public void Execute(string c, int pc = -1)
        {
            int ProgramCounter = pc;

            bool InLoop = false;
            int LoopPc = 0;

            bool Loop = true;
            while (Loop)
            {
                ProgramCounter++;
                if (ProgramCounter >= c.Length)
                {
                    Loop = false;
                    break;
                }
                switch (c[ProgramCounter])
                {
                    case '>':
                        thePointer++;
                        break;
                    case '<':
                        thePointer--;
                        break;
                    case '+':
                        variableArray[thePointer]++;
                        break
                            ;
                    case '-':
                        variableArray[thePointer]--;
                        break;
                    case '.':
                        Console.Write((char)variableArray[thePointer]);
                        break;
                    case ',':
                        variableArray[thePointer] = (byte)(Console.ReadKey().KeyChar);
                        break;
                    case '[':
                        LoopPc = ProgramCounter;
                        break;
                    case ']':

                        if (variableArray[thePointer] != 0)
                        {
                            InLoop = false;
                            LoopPc = 0;
                        }
                        else
                        {
                            ProgramCounter = LoopPc;
                        }

                        break;
                    case '@': //new function: add to stack
                        // 0 = add string
                        // 1 = add int
                        AddToStack(variableArray[thePointer]);

                        break;
                    case '!': //new function: decreace one from stack
                        // 0 = add string
                        Stack.RemoveAt(Stack.Count - 1);

                        break;
                    case '#': //new function: call method
                        //1st topmost on stack = name of method
                        //2st topmost on stack = perams count
                        //3st = perams count topmost on stack = perams
                        Run();

                        break;
                    case '^': //new function: flush aguments

                        for (int i = 0; i < variableArray.Length; i++)
                        {
                            variableArray[i] = 0;
                        }

                        break;
                    default:
                        errorList.Add("Error at char " + ProgramCounter);
                        break;
                }
            }

            foreach (var error in errorList)
            {
                Console.WriteLine(error);
            }

        }

        public void Run()
        {
            var type = Stack[0].ToString();
            try
            {

                var c = new BasicFunctions().GetType();
                List<object> perams = new List<object>();


                int d = Convert.ToInt32(Stack[1]);
                for (int i = 1; i <= d; i++)
                {
                    perams.Add(Stack[i + 1]);
                    Stack.Remove(Stack[i + 1]);
                }
                Stack.RemoveAt(Stack.Count - 1);
                Stack.RemoveAt(Stack.Count - 1);

                try
                {
                    c.InvokeMember(type, BindingFlags.InvokeMethod, null, null, perams.ToArray());
                }
                catch (Exception)
                {


                }
            }
            catch (Exception)
            {

            }
        }

      


        public void AddToStack(int id)
        {
            switch (id)
            {
                case 0:
                    var tmpary = "";
                    //Buffer.BlockCopy(variableArray.ToArray(), thePointer + 1,tmpary,0,variableArray[thePointer + 1]);
                    for (int i = 0; i < variableArray[thePointer + 1]; i++)
                    {
                        tmpary += (char)variableArray[thePointer + 2 + i];
                    }
                    string txt = tmpary;
                    Stack.Add(txt);
                    break;
                case 1:

                    Stack.Add(variableArray[thePointer + 1]);
                    break;

            }
        }
    }
}
